package com.hksj.Intelligentmanagement.service;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hksj.Intelligentmanagement.common.Constants;
import com.hksj.Intelligentmanagement.common.Result;
import com.hksj.Intelligentmanagement.dto.BatchShiftsDTO;
import com.hksj.Intelligentmanagement.dto.ShiftsDTO;
import com.hksj.Intelligentmanagement.dto.ShiftsSimpleDTO;
import com.hksj.Intelligentmanagement.entity.ShiftsEntity;
import com.hksj.Intelligentmanagement.enums.ShiftsEnum;
import com.hksj.Intelligentmanagement.mapper.ShiftsMapper;
import com.hksj.Intelligentmanagement.utils.MemoryPagination;
import com.hksj.Intelligentmanagement.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author zengke
 * @ClassName ShiftsService
 * @Date 2023/3/31 15:28
 * @Version 1.0
 */

@Service
@Slf4j
public class ShiftsService extends ServiceImpl<ShiftsMapper,ShiftsEntity>{

    @Autowired
    private ShiftsMapper shiftsMapper;

    @Autowired
    private Mapper mapper;

    @Autowired
    private NurseService nurseService;

    //进行排班(新增修改和删除一体)
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Result doShifts(ShiftsDTO shiftsDTO){

        //其实是在数据库中插入、修改记录
        //先查询护士在当天是否有进行排班
        // 如果没有,插入这条记录,如果有则修改该班次
        LambdaQueryWrapper<ShiftsEntity> wrapper = new QueryWrapper<ShiftsEntity>().lambda()
                .eq(ShiftsEntity::getNurseId, shiftsDTO.getNurseId())
                .eq(ShiftsEntity::getScheduleTime, shiftsDTO.getScheduleTime());

        ShiftsEntity shiftsEntity = shiftsMapper.selectOne(wrapper);
        if (Objects.isNull(shiftsEntity)){
            //不存在 做创建
            //将shiftsDTO转化为shiftsEntity
            shiftsEntity = mapper.map(shiftsDTO, ShiftsEntity.class);

            //创建一条记录
            shiftsMapper.insert(shiftsEntity);
        }

        //2.存在这条排班记录,对排班记录做更新
        shiftsEntity = mapper.map(shiftsDTO, ShiftsEntity.class);
        //对wrapper查出来的那条数据做更新
        shiftsMapper.update(shiftsEntity,wrapper);
        return Result.ok("操作成功");
    }

    //批量排班
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Result batchShifts(BatchShiftsDTO batchShiftsDTO){

        //获取排班时间集合
        List<String> scheduleTimeList = batchShiftsDTO.getScheduleTimeList();

        //获取逗号拼接的班次id
        String schedulingIds = batchShiftsDTO.getSchedulingIds();

        //获取人员工号集合
        List<String> nurseIdList = batchShiftsDTO.getNurseIdList();

        //创建一个ShiftsDTO实体类接收数据
        ShiftsDTO shiftsDTO = new ShiftsDTO();

        scheduleTimeList.forEach(scheduleTime -> {

                nurseIdList.forEach(nurseId -> {

                    shiftsDTO.setScheduleTime(scheduleTime)
                            .setSchedulingId(schedulingIds)
                            .setNurseId(nurseId);

                    //调用单个的排班方法
                    doShifts(shiftsDTO);
                 });
        });

        return Result.ok("批量排班成功");
    }

    //应用上一周的排班数据
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Result useLastWeekShifts(ShiftsDTO shiftsDTO){
        //将指定时间后移七天即运用上周的排班数据
        shiftsDTO.setMoveDays(7);

        //将前端传来的时间前移7天
        shiftsDTO.setStartTime(moveDate(shiftsDTO.getStartTime(),-7));
        shiftsDTO.setEndTime(moveDate(shiftsDTO.getEndTime(),-7));

        shiftsMapper.useLastWeekShifts(shiftsDTO);

        return Result.ok("运用上周的排班成功");

    }

    //排班前移或后移:前移的话开始时间不会移动到时间范围外
    // 即如果将 2023-04-17~2023-04-23前移1天 04-23会覆盖04-22 04-22会覆盖04-21依次类推 但是04-17不会移动到04-16
    //如果将 2023-04-17~2023-04-23 后移1天 04-17会覆盖04-18 04-18会覆盖04-19 以此类推 但是 04-23不会覆盖04-24
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Result moveShifts(ShiftsDTO shiftsDTO){
        //前端传前移或者后移量,调用排班移动接口进行排版移动
        shiftsMapper.moveShifts(shiftsDTO);
        return Result.ok("排班移动成功");
    }

    //将指定时间向前或向后移动指定天数
    public static String moveDate(String dateStr,int days){

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        Date parse = null;
        try {
            parse = sdf.parse(dateStr);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }

        //将解析好的时间交给日历类处理
        Calendar cal = Calendar.getInstance();
        cal.setTime(parse);

        //若传入的值为负数则前移,正数则后移
        cal.add(Calendar.DATE,days);

        return sdf.format(cal.getTime());

    }

    //查询排班记录
    /**
     * 优化,不要频繁调用数据库
     * @param shiftsDTO
     * @return
     */
    /*public IPage<ShiftsVo> queryShiftsListByPage(ShiftsDTO shiftsDTO){

        //创建一个分页实例
        IPage<ShiftsVo> page = new Page<>(shiftsDTO.getPageNum(),shiftsDTO.getPageSize());

        //先把所有的护士先分页查出来
        List<ShiftsVo> shiftsVos = shiftsMapper.queryNurseListByPage(page, shiftsDTO);

        //查询排班表的记录
        List<ShiftsDetailVo> shiftsDetailVos = shiftsMapper.queryAllShiftsDetail(shiftsDTO);

        //查询班次表的记录
        List<ClassesVo> classesVos = shiftsMapper.queryAllClasses();

        List<ShiftsVo> newShiftsVos = shiftsVos.stream() //将排班记录转为stream流
                .peek(shiftsVo -> {//peek函数,可以对每个shiftsVo对象进行消费操作
                    shiftsVo.setShiftsDetailList(//将排班记录明细设置到排班记录中
                        shiftsDetailVos.stream()//这里是将排班记录列表先转换为stream流,再做过滤
                                //过滤条件为 排版记录明细列表中的每个排班明细的工号与排班记录中的护士工号一致就保留,不一致就舍弃
                            .filter(shiftsDetailVo -> Objects.equals(shiftsDetailVo.getEmployeeNumber(), shiftsVo.getEmployeeNumber()))
                                //同样使用peek函数对排班记录明细对象进行消费操作
                                .peek(shiftsDetailVo -> {
                                    //将班次记录设置到排班记录明细中
                                    shiftsDetailVo.setClassesVoList(
                                            //将班次记录列表转换为stream流,再做过滤
                                            classesVos.stream()
                                                    //过滤条件是 班次记录id存在与排班明细的班次id(这里的id存的是班次的id使用逗号拼接的字符串)中时就保留,不存在舍弃
                                                    .filter(classesVo->shiftsDetailVo.getSchedulingId().contains(classesVo.getId()))
                                                    //回收流,并将流转换为list集合(这里返回的是符合过滤条件的classesVos然后让shiftsDetailVo 设置到classesVoList这个字段内)
                                                    .collect(Collectors.toList())
                                    );
                                })
                                //回收流,并将流转换为list集合(这里返回的是符合过滤条件的shiftsDetailVos 然后让shiftsVo设置到shiftsDetailList这个字段内)
                                .collect(Collectors.toList()));
                })
                //回收流,并将流转换为list集合(这里返回的是shiftsVo消费完后的shiftsVos集合)
                .collect(Collectors.toList());

        //将结果设置到分页中
        page.setRecords(newShiftsVos);

        return page;
    }*/

    /**
     * 以时间为主轴查询排班记录
     * @param shiftsDTO 排班DTO
     * @return 分页
     */
    public IPage<ShiftsVo> queryShiftsListByPage(ShiftsDTO shiftsDTO){

        //创建一个分页实例
        IPage<ShiftsVo> page = new Page<>(shiftsDTO.getPageNum(),shiftsDTO.getPageSize());

        IPage<ShiftsDetailVo> page2 = new Page<>(shiftsDTO.getPageNum(),shiftsDTO.getPageSize());

        MemoryPagination<ShiftsDetailVo> memoryPagination = new MemoryPagination<>(page2);

        //先把输入的时间段内的连续日期查出来
        List<String> durationVos = shiftsMapper.queryDuration(shiftsDTO);

        //将连续时间字段转为ShiftsVo
        List<ShiftsVo> shiftsVosList = durationVos.stream()
                .map(s -> new ShiftsVo().setScheduleTime(s)
                )
                .collect(Collectors.toList());

        //分页查询排班明细
        List<ShiftsDetailVo> shiftsDetailVos = shiftsMapper.queryShiftsDetail(shiftsDTO);

        //查询所有班次记录
        List<ClassesVo> classesVos = shiftsMapper.queryAllClasses();

        List<ShiftsVo> newShiftsVos = shiftsVosList.stream()
                //将排班记录转为stream流
                .peek(shiftsVo -> shiftsVo.setNurseList(//调用peek方法将护士列表set到每一个排班记录中
                        //将排班明细(护士列表)转为stream流 ---使用内存对护士列表进行分页,数量不会特别多不会导致内存溢出的问题
                        memoryPagination.pagination(shiftsDetailVos.stream()
                                //对比排班记录和排班明细的排班时间,若时间相同则保留,不相同就舍弃
                                .filter(shiftsDetailVo -> Objects.equals(shiftsVo.getScheduleTime(),shiftsDetailVo.getScheduleTime()))
                                //在过滤完后调用peek方法将符合条件的班次列表set到每一个排班明细中
                                .peek(shiftsDetailVo -> shiftsDetailVo.setClassesList(classesVos.stream()
                                        //过滤条件:当排班明细的班次id包含班次表中的班次id时保留否则舍弃
                                        .filter(classesVo -> shiftsDetailVo.getSchedulingId().contains(classesVo.getId()))
                                        //回收流,并将流转换为list集合
                                        .collect(Collectors.toList())
                                ))
                                //回收流,并将流转换为list集合
                                .collect(Collectors.toList()),
                                shiftsDTO.getPageNum(),shiftsDTO.getPageSize(),page2)
                ))
                //回收流,并将流转换为list集合
                .collect(Collectors.toList());

        //将结果设置到分页中
        page.setRecords(newShiftsVos)
                .setPages(memoryPagination.getPage().getPages())
                .setTotal(memoryPagination.getPage().getTotal());

        return page;
    }

    /**
     * 每日排班
     * @param shiftsDTO
     */
    public void saveShifts(ShiftsSimpleDTO shiftsDTO) {
        //接受数据的集合
        List<ShiftsEntity> res = new ArrayList<>();
        //获取前端传递数据
        List<ShiftsSimpleDTO.ShiftsItem> items = shiftsDTO.getItems();
        ShiftsEntity tShifts = null;
        if (!ObjectUtils.isEmpty(items)){
            for (ShiftsSimpleDTO.ShiftsItem item : items) {
                if (!ObjectUtils.isEmpty(item.getNurseIdList())){
                    for (String nurseId : item.getNurseIdList()) {
                        tShifts = new ShiftsEntity();
                        tShifts.setScheduleTime(shiftsDTO.getScheduleTime());
                        tShifts.setSchedulingId(item.getScheduleId());
                        tShifts.setNurseId(nurseId);
                        res.add(tShifts);
                    }
                }
            }
        }
        //删除之前的排班数据
        baseMapper.deleteByScheduleTime(shiftsDTO.getScheduleTime());
        //保存新的排班数据
        saveBatch(res);
    }


    /**
     * 根据日期获取排班信息详情
     * @param scheduleTime
     * @return
     */
    public ShiftsSimpleVo getShifts(String scheduleTime) {
        ShiftsSimpleVo shifts = baseMapper.getShifts(scheduleTime);

        //回显3-11班次人员信息
        List<NurseSimpleVo> nurseSimpleVos = nurseService.listUnscheduledNurse(scheduleTime, Constants.TYPE_NURSE);
        if (!ObjectUtils.isEmpty(shifts) && !ObjectUtils.isEmpty(shifts.getItems())){
            ShiftsItemVo other = new ShiftsItemVo();
            other.setScheduleId(ShiftsEnum.other.value());
            other.setNurseList(nurseSimpleVos);
            shifts.getItems().add(other);

            //填充没有数据的排班项
            List<String> shiftsKeys = Arrays.stream(ShiftsEnum.values()).map(ShiftsEnum::value).collect(Collectors.toList());
            shiftsKeys.remove(ShiftsEnum.manage1.value());
            shiftsKeys.remove(ShiftsEnum.manage2.value());
            shiftsKeys.remove(ShiftsEnum.other.value());
            shiftsKeys.remove(ShiftsEnum.operating_time.value());
            List<String> returnKeys = shifts.getItems().stream().map(ShiftsItemVo::getScheduleId).collect(Collectors.toList());
            //判断那些项没有数据
            for (String shiftsKey : shiftsKeys) {
                if (!returnKeys.contains(shiftsKey)){
                    shifts.getItems().add(new ShiftsItemVo(shiftsKey,new ArrayList<>()));
                }
            }
        }
        return shifts;
    }
}
